/*
 * The LevelDB-CLI Project
 * Copyright (C) 2024 JuRuoqwq<BitPasta@163.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "input_prompt.h"

#include "correct_input_table.h"

#include <algorithm> //std::min
#include <unordered_set>

/**
 * Levenshtein单词距离算法
 */
int InputPrompt::LevenshteinDistance(const std::string str1,
    const std::string str2) const
{
    int len1 = str1.length();
    int len2 = str2.length();
    std::vector<std::vector<int>> table(len1 + 1, std::vector<int>(len2 + 1));

    // 第一行和第一列
    for (int i = 0; i <= len1; i++) {
        table[i][0] = i;
    }

    for (int j = 0; j <= len2; j++) {
        table[0][j] = j;
    }

    // 填充动态规划表的其余部分  
    for (int i = 1; i <= len1; i++) {  
        for (int j = 1; j <= len2; j++) {  
            if (str1[i - 1] == str2[j - 1]) {  
                table[i][j] = table[i - 1][j - 1];  
            } else {  
                table[i][j] = std::min({
                    table[i - 1][j] + 1,
                    table[i][j - 1] + 1,
                    table[i - 1][j - 1] + 1
                });
            }  
        }  
    }
    return table[len1][len2];
}

std::string InputPrompt::GetCorrectInput(const std::string input,
    const InputTypes type) const
{
    std::string correctInput;

    switch (type) {
        case COMMAND:
            // 为了照顾v3.0.0之前老用户(?), 做出特殊提示
            // v3.0.0之前，使用show kvs,show keys
            // v3.0.0之后，取消了show keys，改为show all
            correctInput = MatcherImpl(input, COMMAND_TABLE);
            if (correctInput == "prefix" || correctInput == "all") {
                correctInput = "show " + correctInput;
            }
            break;
        case VARIABLE:
            correctInput = MatcherImpl(input, VARIABLE_TABLE);
            break;
        case PARAMETER:
           correctInput = MatcherImpl(input, PARAMETER_TABLE);
            break;
        case VALUE:
            correctInput = MatcherImpl(input, VALUE_TABLE);
            break;
        case SUBCMD:
            correctInput = MatcherImpl(input, SUBCMD_TABLE);
            break;
        default:
            break;
    }
    return correctInput;
}

/**
 * 用于应对LevensteinDistance距离相同的情况
 * 通过字符交集的数量来选择语义上更接近的推荐词
 */
int InputPrompt::CalcIntersectionSize(const std::string str1,
    const std::string str2) const
{
    int result = 0;
    std::unordered_set<char> strSet(str1.begin(), str1.end());
    for (char ch : str2) {
        if (strSet.count(ch) > 0) {
            result++;
        }
    }
    return result;
}

std::string InputPrompt::MatcherImpl(const std::string input,
    const std::vector<std::string> table) const
{
    std::vector<std::string> intersections;
    int maxIntersection = 0;

    // 收集所有最大交集数的字符串
    for (const auto &correctInput : table) {
        int currentIntersection = CalcIntersectionSize(correctInput, input);
        if (currentIntersection > maxIntersection) {
            maxIntersection = currentIntersection;
            // 找到了新的最大值，清空之前的交集
            intersections.clear();
            intersections.push_back(correctInput);
        } else if ((currentIntersection == maxIntersection) &&
            currentIntersection != 0) {
            // 当currentIntersection == 0时，说明没有字母交集
            intersections.push_back(correctInput);
        }
    }

    // 没有交集，说明用户输入严重不符
    if (intersections.empty()) {
        return "";
    }

    int minDistance = INT32_MAX;
    std::string closestMatch;
    for (const auto &intersection : intersections) {
        int currentDistance = LevenshteinDistance(intersection, input);
        if (currentDistance < minDistance) {
            minDistance = currentDistance;
            closestMatch = intersection;
        } else if (currentDistance == minDistance) {
            // 在编辑距离和交集数都相同的情况下
            // 我们也无法得知用户的意图，干脆不要提示(doge)
            return "";
        }
    }
    return closestMatch;
}